﻿using System;
using System.Collections.Generic;
using System.Threading;
using Wisdom.Core;

namespace Wisdom.Core.Repositories
{
    /// <summary>
    /// 仓库上下文.
    /// </summary>
    public abstract class RepositoryContext : Disposable, IRepositoryContext
    {
        #region Private Fields

        private readonly Guid id = Guid.NewGuid();
        private readonly ThreadLocal<Dictionary<Guid, object>> localNewCollection = new ThreadLocal<Dictionary<Guid, object>>(() => new Dictionary<Guid, object>());
        private readonly ThreadLocal<Dictionary<Guid, object>> localModifiedCollection = new ThreadLocal<Dictionary<Guid, object>>(() => new Dictionary<Guid, object>());
        private readonly ThreadLocal<Dictionary<Guid, object>> localDeletedCollection = new ThreadLocal<Dictionary<Guid, object>>(() => new Dictionary<Guid, object>());
        private readonly ThreadLocal<bool> localCommitted = new ThreadLocal<bool>(() => true);

        #endregion

        #region Protected Methods
        /// <summary>
        /// 清空仓库上下文.
        /// </summary>
        /// <remarks>提示：仅可在仓库上下文完成本地提交后才能执行.</remarks>
        protected void ClearRegistrations()
        {
            this.localNewCollection.Value.Clear();
            this.localModifiedCollection.Value.Clear();
            this.localDeletedCollection.Value.Clear();
        }
        protected override void DisposeCore()
        {
            this.localCommitted.Dispose();
            this.localDeletedCollection.Dispose();
            this.localModifiedCollection.Dispose();
            this.localNewCollection.Dispose();
        }
        #endregion

        #region Protected Properties
        /// <summary>
        /// Gets an enumerator which iterates over the collection that contains all the objects need to be added to the repository.
        /// </summary>
        protected IEnumerable<KeyValuePair<Guid, object>> LocalNewCollection
        {
            get { return localNewCollection.Value; }
        }
        /// <summary>
        /// Gets an enumerator which iterates over the collection that contains all the objects need to be modified in the repository.
        /// </summary>
        protected IEnumerable<KeyValuePair<Guid, object>> LocalModifiedCollection
        {
            get { return localModifiedCollection.Value; }
        }
        /// <summary>
        /// Gets an enumerator which iterates over the collection that contains all the objects need to be deleted from the repository.
        /// </summary>
        protected IEnumerable<KeyValuePair<Guid, object>> LocalDeletedCollection
        {
            get { return localDeletedCollection.Value; }
        }
        #endregion

        #region IRepositoryContext Members
        /// <summary>
        /// 获得仓库上下文的ID.
        /// </summary>
        public Guid ID
        {
            get { return id; }
        }
        /// <summary>
        /// 注册一个新增对象到仓库上下文件.
        /// </summary>
        /// <typeparam name="TAggregateRoot">类型：聚合根.</typeparam>
        /// <param name="obj">聚合根对象将注册.</param>
        public virtual void RegisterNew<TAggregateRoot>(TAggregateRoot obj) where TAggregateRoot : class, IAggregateRoot
        {
            if (obj.Id.Equals(Guid.Empty))
                throw new ArgumentException("The Id of the object is empty.", "obj");
            if (localModifiedCollection.Value.ContainsKey(obj.Id))
                throw new InvalidOperationException("The object cannot be registered as a new object since it was marked as modified.");
            if (localNewCollection.Value.ContainsKey(obj.Id))
                throw new InvalidOperationException("The object has already been registered as a new object.");
            localNewCollection.Value.Add(obj.Id, obj);
            localCommitted.Value = false;
        }
        /// <summary>
        /// 注册一个修改对象到仓库上下文件
        /// </summary>
        /// <typeparam name="TAggregateRoot">类型：聚合根.</typeparam>
        /// <param name="obj">聚合根对象.</param>
        public virtual void RegisterModified<TAggregateRoot>(TAggregateRoot obj) where TAggregateRoot : class, IAggregateRoot
        {
            if (obj.Id.Equals(Guid.Empty))
                throw new ArgumentException("The Id of the object is empty.", "obj");
            if (localDeletedCollection.Value.ContainsKey(obj.Id))
                throw new InvalidOperationException("The object cannot be registered as a modified object since it was marked as deleted.");
            if (!localModifiedCollection.Value.ContainsKey(obj.Id) && !localNewCollection.Value.ContainsKey(obj.Id))
                localModifiedCollection.Value.Add(obj.Id, obj);
            localCommitted.Value = false;
        }
        /// <summary>
        /// 注册一个删除对象到仓库上下文件.
        /// </summary>
        /// <typeparam name="TAggregateRoot">类型：聚合根.</typeparam>
        /// <param name="obj">聚合根对象.</param>
        public virtual void RegisterDeleted<TAggregateRoot>(TAggregateRoot obj) where TAggregateRoot : class, IAggregateRoot
        {
            if (obj.Id.Equals(Guid.Empty))
                throw new ArgumentException("The Id of the object is empty.", "obj");
            if (localNewCollection.Value.ContainsKey(obj.Id))
            {
                if (localNewCollection.Value.Remove(obj.Id))
                    return;
            }
            bool removedFromModified = localModifiedCollection.Value.Remove(obj.Id);
            bool addedToDeleted = false;
            if (!localDeletedCollection.Value.ContainsKey(obj.Id))
            {
                localDeletedCollection.Value.Add(obj.Id, obj);
                addedToDeleted = true;
            }
            localCommitted.Value = !(removedFromModified || addedToDeleted);
        }

        #endregion

        #region IUnitOfWork Members
        /// <summary>
        /// 获取一个是否提交的工作单元.
        /// </summary>
        public bool Committed
        {
            get { return localCommitted.Value; }
            protected set { localCommitted.Value = value; }
        }
        /// <summary>
        /// 提交一个工作单元.
        /// </summary>
        public abstract void Commit();
        /// <summary>
        /// 回滚一个工作单元.
        /// </summary>
        public abstract void Rollback();

        #endregion
    }
}
